package com.sad.jetpack.v1.datamodel.api.extension.client.socket.ipc;

import com.sad.jetpack.v1.datamodel.api.utils.CollectionTraverseUtils;
import com.sad.jetpack.v1.datamodel.api.utils.LogcatUtils;

import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class IPCTerminal {

    /**
     * 多对多：
     * ServerSocket监听其他端，每监听到其他端的请求，就生成新线程处理请求，新线程内的socket是一次性、处理一对一的，并保持长链接，
     * 期间双方各种用自己的messenger对象发送信息，用监听器接收信息，直到双方有一方主动断开为止（循环read结束、线程杀死）。端和端之间可以互相访问和监听
     */

    private IPCMember selfMember;//当前端的信息
    private ServerSocket server;//监听其他端的请求生成新会话通路
    private boolean isExit=false;//退出监听的标记
    //一些属性定义
    private int serverPort=12057;//默认端口号
    private int maxClientCount=1000;
    /*private IPCChatMessenger chatMessenger;
    private IPCConnectionProtocolFactory connectionProtocolFactory;*/

    private final Map<String,IPCChatRunnable> ipcChatRunnableMapFromListening =new ConcurrentHashMap<>();

    private IPCTerminal(){

    }

    private IPCTerminal(IPCMember selfMember){
        this.selfMember=selfMember;
    }

    public static IPCTerminal newInstance(IPCMember selfMember){
        return new IPCTerminal(selfMember);
    }

    /*public IPCTerminal listenFileChat(int serverPort, IPCChatFileSessionListener sessionListener) throws Exception{
        DefaultIPCChatFileSession session=new DefaultIPCChatFileSession();
        session.setFileActionListener(sessionListener);
        return listen(serverPort,true,session);
    }
    public IPCTerminal listenImageUrlChat(int serverPort, IPCChatTextSessionListener sessionListener) throws Exception{
        DefaultIPCChatImageUrlSession session=new DefaultIPCChatImageUrlSession();
        session.setTextSessionListener(sessionListener);
        return listen(serverPort,true,session);
    }

    public IPCTerminal listenTextChat(int serverPort, IPCChatTextSessionListener sessionListener) throws Exception{
        DefaultIPCChatTextSession textSession=new DefaultIPCChatTextSession();
        textSession.setTextSessionListener(sessionListener);
        return listen(serverPort, true, new IPCChatClientAccessListener() {
            @Override
            public IPCChatSession onCreateSession(IPCChatHeader header) {
                return null;
            }
        });
    }*/

    /*public IPCTerminal listen(int serverPort, boolean enable_restart,IPCChatSession session) throws Exception{
        AbsIPCChatRunnable chatRunnable = new DefaultIPCChatRunnable(session);
        return listen(serverPort,enable_restart,chatRunnable);
    }*/

    /**
     * 循环监听来自其他端的请求，只要不是主动离开或者异常，就一直监听
     */
    public IPCTerminal listen(int serverPort, boolean enable_restart, IPCChatAccessListener chatAccessListener) throws Exception{

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if (server==null || server.isClosed()){
                        server=new ServerSocket(serverPort,maxClientCount);
                    }
                    else {
                        if (enable_restart){
                            closeSeverSocket(server);
                            server=new ServerSocket(serverPort,maxClientCount);
                        }
                    }
                    if (chatAccessListener!=null){
                        chatAccessListener.onServerSocketCreated(IPCTerminal.this,server);
                    }
                    while (!isExit && server!=null && !server.isClosed()){
                        Socket mirrorClientSocket = server.accept();//阻塞循环
                        LogcatUtils.e("IPC测试-------------->监听端收到连接请求,客户端IP="+mirrorClientSocket.getInetAddress().getHostAddress());
                        //监听到来自其他端的请求,首先验证请求的有效性，保证链接可靠，并询问这条socket的目的，也即是TCP的握手
                        IPCChatMessenger chatMessenger=new DefaultIPCChatMessenger(mirrorClientSocket);
                        IPCConnectionProtocolFactory connectionProtocolFactory=new DefaultIPCConnectionProtocolFactory(chatMessenger);
                        boolean vh = connectionProtocolFactory.checkSocketAvailability(mirrorClientSocket);
                        LogcatUtils.e("IPC测试-------------->监听端验证客户端的有效性："+vh);
                        if (vh){
                            //链接有效,通知对方进行第二次握手了，第二次握手成立的话就会建立会话，注意会话一旦建立，对方的信息就已经确定，建立的链接通路就专供双方一对一对话。
                            IPCChatHeader header= connectionProtocolFactory.getHeader(mirrorClientSocket);
                            if (header!=null){
                                LogcatUtils.e("IPC测试-------------->监听端收到客户端的头信息："+header.toJson().toString(4));
                                AbsIPCChatRunnable chatRunnable = new DefaultIPCChatRunnable();
                                IPCChatSession chatSession= new DefaultIPCChatTextSession();
                                if (chatAccessListener!=null){
                                    boolean authValid=chatAccessListener.onCheckAccessPermission(header);
                                    if (!authValid){
                                        //无效链接，非法数据等等，未授权的端....，此时放弃链接
                                        connectionProtocolFactory.notifyError(mirrorClientSocket,new ErrorMessage(10004,"鉴权错误，非法链接！"));
                                        continue;
                                    }
                                    IPCChatSession cs=chatAccessListener.onCreateSession(header);
                                    if (cs!=null){
                                        chatSession=cs;
                                    }
                                }
                                chatRunnable.setChatSession(chatSession);
                                int currChatDataTypeCode=header.getIntent().getChatDataType().getTypeCode();
                                int chatFutureDataType=chatRunnable.getChatSession().chatDataType().getTypeCode();
                                if (chatFutureDataType==currChatDataTypeCode){
                                    LogcatUtils.e("IPC测试-------------->监听端通知客户端头信息有效，监听端准备接收正式数据，数据类型约定："+chatFutureDataType);
                                    //通知对方建立读写双向通路，准备正式对话
                                    connectionProtocolFactory.notifyReadyToChat(mirrorClientSocket);
                                    chatRunnable.setChatMessenger(chatMessenger);
                                    chatRunnable.setHeader(header);
                                    chatRunnable.setSelfMember(selfMember);
                                    chatRunnable.setSocket(mirrorClientSocket);
                                    chatRunnable.setExceptionListener(new IPCInternalExceptionListener() {
                                        @Override
                                        public void onChatRunnableException(IPCMember selfMember,Exception e) {
                                            if (selfMember!=null){
                                                closeChatRunnable(selfMember.getClientId());
                                            }
                                        }
                                    });
                                    // 分配一个专用socket对象来建立信道,并新建线程来运行socket会话
                                    ipcChatRunnableMapFromListening.put(selfMember.getClientId(),chatRunnable);
                                    new Thread(chatRunnable).start();
                                }
                                else {
                                    //监听到的请求类型和预置的session类型不一致
                                    connectionProtocolFactory.notifyError(mirrorClientSocket,new ErrorMessage(10001,"请求预期的交互数据类型错误，端口"+serverPort+"处理的交互数据类型是:"+chatFutureDataType));
                                }

                            }
                            else {
                                //无效链接，非法数据等等，未授权的端....，此时放弃链接
                                connectionProtocolFactory.notifyError(mirrorClientSocket,new ErrorMessage(10002,"头信息内容解析失败或者未设置chatRunnable"));
                            }
                        }
                        else {
                            //无效链接，非法数据等等，未授权的端....，此时放弃链接
                            connectionProtocolFactory.notifyError(mirrorClientSocket,new ErrorMessage(10003,"有效数据口令错误"));
                        }

                    }
                }catch (Exception e){
                    e.printStackTrace();
                    //流异常、socket关闭异常、端口占用异常等等
                }
            }
        }).start();
        return this;
    }


    private void closeChatRunnable(String id){
        try {
            IPCChatRunnable chatRunnable= ipcChatRunnableMapFromListening.get(id);
            ipcChatRunnableMapFromListening.remove(id);
            if (chatRunnable!=null){
                chatRunnable.close();
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void closeChatRunnable(IPCChatRunnable chat){
        try {
            chat.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void closeSeverSocket(ServerSocket socket){
        try {
            if (socket!=null && !socket.isClosed()){
                socket.close();
            }
            CollectionTraverseUtils.traverseMap(ipcChatRunnableMapFromListening, new CollectionTraverseUtils.ITraverseAction<String, IPCChatRunnable>() {
                @Override
                public void onTraversed(String s, IPCChatRunnable ipcChatRunnable) throws Exception {
                    ipcChatRunnable.close();
                }
            });
            ipcChatRunnableMapFromListening.clear();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void connect(String ip, int port, IPCChatHeader header, IPCChatFileSessionListener sessionListener,IPCNewChatErrorMessageListener errorMessageListener) throws Exception{
        DefaultIPCChatFileSession fileSession=new DefaultIPCChatFileSession();
        fileSession.setFileActionListener(sessionListener);
        connect(ip,port,header,fileSession,errorMessageListener);
    }

    /*public static void createImageUrlChat(String ip, int port, IPCChatHeader header, IPCChatTextSessionListener sessionListener,IPCNewChatErrorMessageListener errorMessageListener) throws Exception{
        DefaultIPCChatImageUrlSession imageUrlSession=new DefaultIPCChatImageUrlSession();
        imageUrlSession.setTextSessionListener(sessionListener);
        connect(ip,port,header,imageUrlSession,errorMessageListener);
    }*/
    public static void connect(String ip, int port, IPCChatHeader header, IPCChatTextSessionListener sessionListener, IPCNewChatErrorMessageListener errorMessageListener) throws Exception{
        DefaultIPCChatTextSession textSession=new DefaultIPCChatTextSession();
        textSession.setTextSessionListener(sessionListener);
        connect(ip,port,header,textSession,errorMessageListener);
    }
    public static void connect(String ip, int port, IPCChatHeader header, IPCChatSession session, IPCNewChatErrorMessageListener errorMessageListener) throws Exception{
        AbsIPCChatRunnable chatRunnable = new DefaultIPCChatRunnable(session);
        connect(ip,port,header,chatRunnable,errorMessageListener);
    }
    public static void connect(String ip, int port, IPCChatHeader header, AbsIPCChatRunnable chatRunnable, IPCNewChatErrorMessageListener errorMessageListener) throws Exception{
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Socket socket=new Socket(ip,port);
                    LogcatUtils.e("IPC测试-------------->客户端发起连接,IP="+ip+",PORT="+port+",成功="+socket.isConnected());
                    IPCChatMessenger chatMessenger=new DefaultIPCChatMessenger(socket);
                    //先主动发送一条证明自身合法性的验证头
                    OutputStream outputStream=socket.getOutputStream();
                    outputStream.write(DefaultIPCConnectionProtocolFactory.CMD_SOCKET_AVAILABLE_MARK.getBytes(StandardCharsets.UTF_8));
                    //获取监听端的许可，监听端明确要求客户端发送头信息
                    byte[] shb=chatMessenger.syncReceive(-1);
                    String sh=new String(shb,StandardCharsets.UTF_8);
                    if (!DefaultIPCConnectionProtocolFactory.CMD_SOCKET_GET_HEADER.equals(sh)){
                        LogcatUtils.e("IPC测试-------------->获取监听端许可异常："+sh);
                        return;
                    }
                    LogcatUtils.e("IPC测试-------------->客户端准备发送头信息："+header.toJson().toString(4));
                    //然后继续发送一条头信息,并读取对方的回应
                    byte[] rp=chatMessenger.syncSend(header.toJson().toString().getBytes(StandardCharsets.UTF_8),1024,true);
                    String rps=new String(rp,StandardCharsets.UTF_8);
                    LogcatUtils.e("IPC测试-------------->客户端获取监听端对头信息的反馈："+rps);
                    if (chatRunnable!=null){
                        chatRunnable.setSelfMember(header.getFrom());
                        chatRunnable.setChatMessenger(chatMessenger);
                        chatRunnable.setSocket(socket);
                        chatRunnable.setHeader(header);
                    }
                    if (DefaultIPCConnectionProtocolFactory.CMD_SOCKET_READY_TO_CHAT.equals(rps)){
                        /*int chatType=header.getIntent().getType();
                        //对方认定此次会话合法，开始对话，主动发送数据
                        */
                        if (chatRunnable!=null){
                            chatRunnable.run();
                        }
                    }
                    else if (DefaultIPCConnectionProtocolFactory.CMD_SOCKET_ERROR.equals(rps)){
                        //服务端返回错误信息
                        byte[] er=chatMessenger.syncReceive(1024);
                        ErrorMessage errorMessage=ErrorMessage.parse(er);
                        if (errorMessageListener!=null && chatRunnable!=null && chatRunnable.getSelfMember()!=null){
                            errorMessageListener.onReceivedErrorMessage(chatRunnable.getSelfMember(),errorMessage);
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void joinGroup(String ip,int port){

    }



}
